home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / iceweasel / modules / domplate.jsm < prev    next >
Encoding:
Text File  |  2013-01-09  |  48.0 KB  |  1,892 lines

  1. /*
  2.  * Software License Agreement (BSD License)
  3.  *
  4.  * Copyright (c) 2007, Parakey Inc.
  5.  * All rights reserved.
  6.  *
  7.  * Redistribution and use of this software in source and binary forms, with or without modification,
  8.  * are permitted provided that the following conditions are met:
  9.  *
  10.  * * Redistributions of source code must retain the above
  11.  *   copyright notice, this list of conditions and the
  12.  *   following disclaimer.
  13.  *
  14.  * * Redistributions in binary form must reproduce the above
  15.  *   copyright notice, this list of conditions and the
  16.  *   following disclaimer in the documentation and/or other
  17.  *   materials provided with the distribution.
  18.  *
  19.  * * Neither the name of Parakey Inc. nor the names of its
  20.  *   contributors may be used to endorse or promote products
  21.  *   derived from this software without specific prior
  22.  *   written permission of Parakey Inc.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  25.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  26.  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  27.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  30.  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  31.  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32.  */
  33.  
  34. /*
  35.  * Creator:
  36.  *  Joe Hewitt
  37.  * Contributors:
  38.  *  John J. Barton (IBM Almaden)
  39.  *  Jan Odvarko (Mozilla Corp.)
  40.  *  Max Stepanov (Aptana Inc.)
  41.  *  Rob Campbell (Mozilla Corp.)
  42.  *  Hans Hillen (Paciello Group, Mozilla)
  43.  *  Curtis Bartley (Mozilla Corp.)
  44.  *  Mike Collins (IBM Almaden)
  45.  *  Kevin Decker
  46.  *  Mike Ratcliffe (Comartis AG)
  47.  *  Hernan Rodr├¡guez Colmeiro
  48.  *  Austin Andrews
  49.  *  Christoph Dorn
  50.  *  Steven Roussey (AppCenter Inc, Network54)
  51.  * Firefox Port Contributors:
  52.  *  Rob Campbell
  53.  *  Julian Viereck
  54.  *  Mihai Sucan
  55.  */
  56.  
  57. var EXPORTED_SYMBOLS = ["domplate", "HTMLTemplates", "domplateUtils"];
  58.  
  59. const Ci = Components.interfaces;
  60. const Cu = Components.utils;
  61.  
  62. const invisibleTags = {
  63.   "head": true,
  64.   "base": true,
  65.   "basefont": true,
  66.   "isindex": true,
  67.   "link": true,
  68.   "meta": true,
  69.   "script": true,
  70.   "style": true,
  71.   "title": true,
  72. };
  73.  
  74. // End tags for void elements are forbidden
  75. // http://wiki.whatwg.org/wiki/HTML_vs._XHTML
  76. const selfClosingTags = {
  77.   "meta": 1,
  78.   "link": 1,
  79.   "area": 1,
  80.   "base": 1,
  81.   "col": 1,
  82.   "input": 1,
  83.   "img": 1,
  84.   "br": 1,
  85.   "hr": 1,
  86.   "param": 1,
  87.   "embed": 1
  88. };
  89.  
  90. const reNotWhitespace = /[^\s]/;
  91. const showTextNodesWithWhitespace = false;
  92.  
  93. var DOM = {};
  94. var domplateUtils = {};
  95.  
  96. /**
  97.  * Utility function to allow outside caller to set a global scope within
  98.  * domplate's DOM object. Specifically for access to DOM constants and classes.
  99.  * @param aGlobal
  100.  *        The global object whose scope we wish to capture.
  101.  */
  102. domplateUtils.setDOM = function(aGlobal)
  103. {
  104.   DOM = aGlobal;
  105.   if (!aGlobal) {
  106.     womb = null;
  107.   }
  108. };
  109.  
  110. /**
  111.  * main domplate constructor function.
  112.  */
  113.  
  114. let domplate = function()
  115. {
  116.   let lastSubject;
  117.   for (let i = 0; i < arguments.length; ++i)
  118.     lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
  119.  
  120.   for (let name in lastSubject) {
  121.     let val = lastSubject[name];
  122.     if (isTag(val))
  123.       val.tag.subject = lastSubject;
  124.   }
  125.  
  126.   return lastSubject;
  127. };
  128.  
  129. var womb = null;
  130.  
  131. ///////////////////////////////////////////////////////////////////////////
  132. //// Base functions
  133.  
  134. function DomplateTag(tagName)
  135. {
  136.   this.tagName = tagName;
  137. }
  138.  
  139. function DomplateEmbed()
  140. {
  141. }
  142.  
  143. function DomplateLoop()
  144. {
  145. }
  146.  
  147. ///////////////////////////////////////////////////////////////////////////
  148. //// Definitions
  149.  
  150. domplate.context = function(context, fn)
  151. {
  152.   let lastContext = domplate.lastContext;
  153.   domplate.topContext = context;
  154.   fn.apply(context);
  155.   domplate.topContext = lastContext;
  156. };
  157.  
  158. domplate.TAG = function()
  159. {
  160.   let embed = new DomplateEmbed();
  161.   return embed.merge(arguments);
  162. };
  163.  
  164. domplate.FOR = function()
  165. {
  166.   let loop = new DomplateLoop();
  167.   return loop.merge(arguments);
  168. };
  169.  
  170. DomplateTag.prototype =
  171. {
  172.   /**
  173.    * Initializer for DOM templates. Called to create new Functions objects
  174.    * like TR, TD, OBJLINK, etc. See defineTag
  175.    * @param args keyword argments for the template, the {} brace stuff after
  176.    *        the tag name, eg TR({...}, TD(...
  177.    * @param oldTag a nested tag, eg the TD tag in TR({...}, TD(...
  178.    */
  179.   merge: function(args, oldTag)
  180.   {
  181.     if (oldTag)
  182.       this.tagName = oldTag.tagName;
  183.  
  184.     this.context = oldTag ? oldTag.context : null;  // normally null on construction
  185.     this.subject = oldTag ? oldTag.subject : null;
  186.     this.attrs = oldTag ? copyObject(oldTag.attrs) : {};
  187.     this.classes = oldTag ? copyObject(oldTag.classes) : {};
  188.     this.props = oldTag ? copyObject(oldTag.props) : null;
  189.     this.listeners = oldTag ? copyArray(oldTag.listeners) : null;
  190.     this.children = oldTag ? copyArray(oldTag.children) : [];
  191.     this.vars = oldTag ? copyArray(oldTag.vars) : [];
  192.  
  193.     let attrs = args.length ? args[0] : null;
  194.     let hasAttrs = typeof(attrs) == "object" && !isTag(attrs);
  195.  
  196.     // Do not clear children, they can be copied from the oldTag.
  197.     //this.children = [];
  198.  
  199.     if (domplate.topContext)
  200.       this.context = domplate.topContext;
  201.  
  202.     if (args.length)
  203.       parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children);
  204.  
  205.     if (hasAttrs)
  206.       this.parseAttrs(attrs);
  207.  
  208.     return creator(this, DomplateTag);
  209.   },
  210.  
  211.   /**
  212.    * Parse node attributes.
  213.    * @param args
  214.    *        Object of arguments to process.
  215.    */
  216.   parseAttrs: function(args)
  217.   {
  218.     for (let name in args) {
  219.       let val = parseValue(args[name]);
  220.       readPartNames(val, this.vars);
  221.  
  222.       if (name.indexOf("on") == 0) {
  223.         let eventName = name.substr(2);
  224.         if (!this.listeners)
  225.           this.listeners = [];
  226.         this.listeners.push(eventName, val);
  227.       } else if (name[0] == "_") {
  228.         let propName = name.substr(1);
  229.         if (!this.props)
  230.           this.props = {};
  231.         this.props[propName] = val;
  232.       } else if (name[0] == "$") {
  233.         let className = name.substr(1);
  234.         if (!this.classes)
  235.           this.classes = {};
  236.         this.classes[className] = val;
  237.       } else {
  238.         if (name == "class" && this.attrs.hasOwnProperty(name))
  239.           this.attrs[name] += " " + val;
  240.         else
  241.           this.attrs[name] = val;
  242.       }
  243.     }
  244.   },
  245.  
  246.   compile: function()
  247.   {
  248.     if (this.renderMarkup)
  249.       return;
  250.  
  251.     this.compileMarkup();
  252.     this.compileDOM();
  253.   },
  254.  
  255.   compileMarkup: function()
  256.   {
  257.     this.markupArgs = [];
  258.     let topBlock = [], topOuts = [], blocks = [],
  259.       info = {args: this.markupArgs, argIndex: 0};
  260.  
  261.     this.generateMarkup(topBlock, topOuts, blocks, info);
  262.     this.addCode(topBlock, topOuts, blocks);
  263.  
  264.     let fnBlock = ['(function (__code__, __context__, __in__, __out__'];
  265.     for (let i = 0; i < info.argIndex; ++i)
  266.       fnBlock.push(', s', i);
  267.     fnBlock.push(') {\n');
  268.  
  269.     if (this.subject)
  270.       fnBlock.push('with (this) {\n');
  271.     if (this.context)
  272.       fnBlock.push('with (__context__) {\n');
  273.     fnBlock.push('with (__in__) {\n');
  274.  
  275.     fnBlock.push.apply(fnBlock, blocks);
  276.  
  277.     if (this.subject)
  278.       fnBlock.push('}\n');
  279.     if (this.context)
  280.       fnBlock.push('}\n');
  281.  
  282.     fnBlock.push('}})\n');
  283.  
  284.     function __link__(tag, code, outputs, args)
  285.     {
  286.       tag.tag.compile();
  287.  
  288.       let tagOutputs = [];
  289.       let markupArgs = [code, tag.tag.context, args, tagOutputs];
  290.       markupArgs.push.apply(markupArgs, tag.tag.markupArgs);
  291.       tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs);
  292.  
  293.       outputs.push(tag);
  294.       outputs.push(tagOutputs);
  295.     }
  296.  
  297.     function __escape__(value)
  298.     {
  299.       function replaceChars(ch)
  300.       {
  301.         switch (ch) {
  302.           case "<":
  303.             return "<";
  304.           case ">":
  305.             return ">";
  306.           case "&":
  307.             return "&";
  308.           case "'":
  309.             return "'";
  310.           case '"':
  311.             return """;
  312.         }
  313.         return "?";
  314.       };
  315.       return String(value).replace(/[<>&"']/g, replaceChars);
  316.     }
  317.  
  318.     function __loop__(iter, outputs, fn)
  319.     {
  320.       let iterOuts = [];
  321.       outputs.push(iterOuts);
  322.  
  323.       if (iter instanceof Array)
  324.         iter = new ArrayIterator(iter);
  325.  
  326.       try {
  327.         while (1) {
  328.           let value = iter.next();
  329.           let itemOuts = [0, 0];
  330.           iterOuts.push(itemOuts);
  331.           fn.apply(this, [value, itemOuts]);
  332.         }
  333.       } catch (exc) {
  334.         if (exc != StopIteration)
  335.           throw exc;
  336.       }
  337.     }
  338.  
  339.     let js = fnBlock.join("");
  340.     this.renderMarkup = eval(js);
  341.   },
  342.  
  343.   getVarNames: function(args)
  344.   {
  345.     if (this.vars)
  346.       args.push.apply(args, this.vars);
  347.  
  348.     for (let i = 0; i < this.children.length; ++i) {
  349.       let child = this.children[i];
  350.       if (isTag(child))
  351.         child.tag.getVarNames(args);
  352.       else if (child instanceof Parts) {
  353.         for (let i = 0; i < child.parts.length; ++i) {
  354.           if (child.parts[i] instanceof Variable) {
  355.             let name = child.parts[i].name;
  356.             let names = name.split(".");
  357.             args.push(names[0]);
  358.           }
  359.         }
  360.       }
  361.     }
  362.   },
  363.  
  364.   generateMarkup: function(topBlock, topOuts, blocks, info)
  365.   {
  366.     topBlock.push(',"<', this.tagName, '"');
  367.  
  368.     for (let name in this.attrs) {
  369.       if (name != "class") {
  370.         let val = this.attrs[name];
  371.         topBlock.push(', " ', name, '=\\""');
  372.         addParts(val, ',', topBlock, info, true);
  373.         topBlock.push(', "\\""');
  374.       }
  375.     }
  376.  
  377.     if (this.listeners) {
  378.       for (let i = 0; i < this.listeners.length; i += 2)
  379.         readPartNames(this.listeners[i+1], topOuts);
  380.     }
  381.  
  382.     if (this.props) {
  383.       for (let name in this.props)
  384.         readPartNames(this.props[name], topOuts);
  385.     }
  386.  
  387.     if (this.attrs.hasOwnProperty("class") || this.classes) {
  388.       topBlock.push(', " class=\\""');
  389.       if (this.attrs.hasOwnProperty("class"))
  390.         addParts(this.attrs["class"], ',', topBlock, info, true);
  391.       topBlock.push(', " "');
  392.       for (let name in this.classes) {
  393.         topBlock.push(', (');
  394.         addParts(this.classes[name], '', topBlock, info);
  395.         topBlock.push(' ? "', name, '" + " " : "")');
  396.       }
  397.       topBlock.push(', "\\""');
  398.     }
  399.     topBlock.push(',">"');
  400.  
  401.     this.generateChildMarkup(topBlock, topOuts, blocks, info);
  402.     topBlock.push(',"</', this.tagName, '>"');
  403.   },
  404.  
  405.   generateChildMarkup: function(topBlock, topOuts, blocks, info)
  406.   {
  407.     for (let i = 0; i < this.children.length; ++i) {
  408.       let child = this.children[i];
  409.       if (isTag(child))
  410.         child.tag.generateMarkup(topBlock, topOuts, blocks, info);
  411.       else
  412.         addParts(child, ',', topBlock, info, true);
  413.     }
  414.   },
  415.  
  416.   addCode: function(topBlock, topOuts, blocks)
  417.   {
  418.     if (topBlock.length)
  419.       blocks.push('__code__.push(""', topBlock.join(""), ');\n');
  420.     if (topOuts.length)
  421.       blocks.push('__out__.push(', topOuts.join(","), ');\n');
  422.     topBlock.splice(0, topBlock.length);
  423.     topOuts.splice(0, topOuts.length);
  424.   },
  425.  
  426.   addLocals: function(blocks)
  427.   {
  428.     let varNames = [];
  429.     this.getVarNames(varNames);
  430.  
  431.     let map = {};
  432.     for (let i = 0; i < varNames.length; ++i) {
  433.       let name = varNames[i];
  434.       if ( map.hasOwnProperty(name) )
  435.         continue;
  436.  
  437.       map[name] = 1;
  438.       let names = name.split(".");
  439.       blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';\n');
  440.     }
  441.   },
  442.  
  443.   compileDOM: function()
  444.   {
  445.     let path = [];
  446.     let blocks = [];
  447.     this.domArgs = [];
  448.     path.embedIndex = 0;
  449.     path.loopIndex = 0;
  450.     path.staticIndex = 0;
  451.     path.renderIndex = 0;
  452.     let nodeCount = this.generateDOM(path, blocks, this.domArgs);
  453.  
  454.     let fnBlock = ['(function (root, context, o'];
  455.  
  456.     for (let i = 0; i < path.staticIndex; ++i)
  457.       fnBlock.push(', ', 's'+i);
  458.  
  459.     for (let i = 0; i < path.renderIndex; ++i)
  460.       fnBlock.push(', ', 'd'+i);
  461.  
  462.     fnBlock.push(') {\n');
  463.     for (let i = 0; i < path.loopIndex; ++i)
  464.       fnBlock.push('var l', i, ' = 0;\n');
  465.     for (let i = 0; i < path.embedIndex; ++i)
  466.       fnBlock.push('var e', i, ' = 0;\n');
  467.  
  468.     if (this.subject)
  469.       fnBlock.push('with (this) {\n');
  470.     if (this.context)
  471.       fnBlock.push('with (context) {\n');
  472.  
  473.     fnBlock.push(blocks.join(""));
  474.  
  475.     if (this.subject)
  476.       fnBlock.push('}\n');
  477.     if (this.context)
  478.       fnBlock.push('}\n');
  479.  
  480.     fnBlock.push('return ', nodeCount, ';\n');
  481.     fnBlock.push('})\n');
  482.  
  483.     function __bind__(object, fn)
  484.     {
  485.       return function(event) { return fn.apply(object, [event]); }
  486.     }
  487.  
  488.     function __link__(node, tag, args)
  489.     {
  490.       if (!tag || !tag.tag)
  491.         return;
  492.  
  493.       tag.tag.compile();
  494.  
  495.       let domArgs = [node, tag.tag.context, 0];
  496.       domArgs.push.apply(domArgs, tag.tag.domArgs);
  497.       domArgs.push.apply(domArgs, args);
  498.  
  499.       return tag.tag.renderDOM.apply(tag.tag.subject, domArgs);
  500.     }
  501.  
  502.     function __loop__(iter, fn)
  503.     {
  504.       let nodeCount = 0;
  505.       for (let i = 0; i < iter.length; ++i) {
  506.         iter[i][0] = i;
  507.         iter[i][1] = nodeCount;
  508.         nodeCount += fn.apply(this, iter[i]);
  509.       }
  510.       return nodeCount;
  511.     }
  512.  
  513.     function __path__(parent, offset)
  514.     {
  515.       let root = parent;
  516.  
  517.       for (let i = 2; i < arguments.length; ++i) {
  518.         let index = arguments[i];
  519.         if (i == 3)
  520.           index += offset;
  521.  
  522.         if (index == -1)
  523.           parent = parent.parentNode;
  524.         else
  525.           parent = parent.childNodes[index];
  526.       }
  527.  
  528.       return parent;
  529.     }
  530.     let js = fnBlock.join("");
  531.     // Exceptions on this line are often in the eval
  532.     this.renderDOM = eval(js);
  533.   },
  534.  
  535.   generateDOM: function(path, blocks, args)
  536.   {
  537.     if (this.listeners || this.props)
  538.       this.generateNodePath(path, blocks);
  539.  
  540.     if (this.listeners) {
  541.       for (let i = 0; i < this.listeners.length; i += 2) {
  542.         let val = this.listeners[i+1];
  543.         let arg = generateArg(val, path, args);
  544.         blocks.push('node.addEventListener("', this.listeners[i],
  545.           '", __bind__(this, ', arg, '), false);\n');
  546.       }
  547.     }
  548.  
  549.     if (this.props) {
  550.       for (let name in this.props) {
  551.         let val = this.props[name];
  552.         let arg = generateArg(val, path, args);
  553.         blocks.push('node.', name, ' = ', arg, ';\n');
  554.       }
  555.     }
  556.  
  557.     this.generateChildDOM(path, blocks, args);
  558.     return 1;
  559.   },
  560.  
  561.   generateNodePath: function(path, blocks)
  562.   {
  563.     blocks.push("var node = __path__(root, o");
  564.     for (let i = 0; i < path.length; ++i)
  565.       blocks.push(",", path[i]);
  566.     blocks.push(");\n");
  567.   },
  568.  
  569.   generateChildDOM: function(path, blocks, args)
  570.   {
  571.     path.push(0);
  572.     for (let i = 0; i < this.children.length; ++i) {
  573.       let child = this.children[i];
  574.       if (isTag(child))
  575.         path[path.length - 1] += '+' + child.tag.generateDOM(path, blocks, args);
  576.       else
  577.         path[path.length - 1] += '+1';
  578.     }
  579.     path.pop();
  580.   },
  581.  
  582.   /*
  583.    * We are just hiding from javascript.options.strict. For some reasons it's
  584.    * ok if we return undefined here.
  585.    * @return null or undefined or possibly a context.
  586.    */
  587.   getContext: function()
  588.   {
  589.     return this.context;
  590.   }
  591. };
  592.  
  593. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  594.  
  595. DomplateEmbed.prototype = copyObject(DomplateTag.prototype,
  596. {
  597.   merge: function(args, oldTag)
  598.   {
  599.     this.value = oldTag ? oldTag.value : parseValue(args[0]);
  600.     this.attrs = oldTag ? oldTag.attrs : {};
  601.     this.vars = oldTag ? copyArray(oldTag.vars) : [];
  602.  
  603.     let attrs = args[1];
  604.     for (let name in attrs) {
  605.       let val = parseValue(attrs[name]);
  606.       this.attrs[name] = val;
  607.       readPartNames(val, this.vars);
  608.     }
  609.  
  610.     return creator(this, DomplateEmbed);
  611.   },
  612.  
  613.   getVarNames: function(names)
  614.   {
  615.     if (this.value instanceof Parts)
  616.       names.push(this.value.parts[0].name);
  617.  
  618.     if (this.vars)
  619.       names.push.apply(names, this.vars);
  620.   },
  621.  
  622.   generateMarkup: function(topBlock, topOuts, blocks, info)
  623.   {
  624.     this.addCode(topBlock, topOuts, blocks);
  625.  
  626.     blocks.push('__link__(');
  627.     addParts(this.value, '', blocks, info);
  628.     blocks.push(', __code__, __out__, {\n');
  629.  
  630.     let lastName = null;
  631.     for (let name in this.attrs) {
  632.       if (lastName)
  633.         blocks.push(',');
  634.       lastName = name;
  635.  
  636.       let val = this.attrs[name];
  637.       blocks.push('"', name, '":');
  638.       addParts(val, '', blocks, info);
  639.     }
  640.  
  641.     blocks.push('});\n');
  642.   },
  643.  
  644.   generateDOM: function(path, blocks, args)
  645.   {
  646.     let embedName = 'e' + path.embedIndex++;
  647.  
  648.     this.generateNodePath(path, blocks);
  649.  
  650.     let valueName = 'd' + path.renderIndex++;
  651.     let argsName = 'd' + path.renderIndex++;
  652.     blocks.push(embedName + ' = __link__(node, ', valueName, ', ',
  653.       argsName, ');\n');
  654.  
  655.     return embedName;
  656.   }
  657. });
  658.  
  659. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  660.  
  661. DomplateLoop.prototype = copyObject(DomplateTag.prototype,
  662. {
  663.   merge: function(args, oldTag)
  664.   {
  665.     this.isLoop = true;
  666.     this.varName = oldTag ? oldTag.varName : args[0];
  667.     this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
  668.     this.vars = [];
  669.  
  670.     this.children = oldTag ? copyArray(oldTag.children) : [];
  671.  
  672.     let offset = Math.min(args.length, 2);
  673.     parseChildren(args, offset, this.vars, this.children);
  674.  
  675.     return creator(this, DomplateLoop);
  676.   },
  677.  
  678.   getVarNames: function(names)
  679.   {
  680.     if (this.iter instanceof Parts)
  681.       names.push(this.iter.parts[0].name);
  682.  
  683.     DomplateTag.prototype.getVarNames.apply(this, [names]);
  684.   },
  685.  
  686.   generateMarkup: function(topBlock, topOuts, blocks, info)
  687.   {
  688.     this.addCode(topBlock, topOuts, blocks);
  689.  
  690.     let iterName;
  691.     if (this.iter instanceof Parts) {
  692.       let part = this.iter.parts[0];
  693.       iterName = part.name;
  694.  
  695.       if (part.format) {
  696.         for (let i = 0; i < part.format.length; ++i)
  697.           iterName = part.format[i] + "(" + iterName + ")";
  698.       }
  699.     } else
  700.       iterName = this.iter;
  701.  
  702.     blocks.push('__loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {\n');
  703.     this.generateChildMarkup(topBlock, topOuts, blocks, info);
  704.     this.addCode(topBlock, topOuts, blocks);
  705.     blocks.push('}]);\n');
  706.   },
  707.  
  708.   generateDOM: function(path, blocks, args)
  709.   {
  710.     let iterName = 'd' + path.renderIndex++;
  711.     let counterName = 'i' + path.loopIndex;
  712.     let loopName = 'l' + path.loopIndex++;
  713.  
  714.     if (!path.length)
  715.       path.push(-1, 0);
  716.  
  717.     let preIndex = path.renderIndex;
  718.     path.renderIndex = 0;
  719.  
  720.     let nodeCount = 0;
  721.  
  722.     let subBlocks = [];
  723.     let basePath = path[path.length-1];
  724.     for (let i = 0; i < this.children.length; ++i) {
  725.       path[path.length - 1] = basePath + '+' + loopName + '+' + nodeCount;
  726.  
  727.       let child = this.children[i];
  728.       if (isTag(child))
  729.         nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);
  730.       else
  731.         nodeCount += '+1';
  732.     }
  733.  
  734.     path[path.length - 1] = basePath + '+' + loopName;
  735.  
  736.     blocks.push(loopName,' = __loop__.apply(this, [',
  737.       iterName, ', function(', counterName, ',', loopName);
  738.     for (let i = 0; i < path.renderIndex; ++i)
  739.       blocks.push(',d' + i);
  740.     blocks.push(') {\n');
  741.     blocks.push(subBlocks.join(""));
  742.     blocks.push('return ', nodeCount, ';\n');
  743.     blocks.push('}]);\n');
  744.  
  745.     path.renderIndex = preIndex;
  746.  
  747.     return loopName;
  748.   }
  749. });
  750.  
  751. ///////////////////////////////////////////////////////////////////////////
  752.  
  753. function Variable(name, format)
  754. {
  755.   this.name = name;
  756.   this.format = format;
  757. }
  758.  
  759. function Parts(parts)
  760. {
  761.   this.parts = parts;
  762. }
  763.  
  764. ///////////////////////////////////////////////////////////////////////////
  765.  
  766. function parseParts(str)
  767. {
  768.   let re = /\$([_A-Za-z][_A-Za-z0-9.|]*)/g;
  769.   let index = 0;
  770.   let parts = [];
  771.  
  772.   let m;
  773.   while (m = re.exec(str)) {
  774.     let pre = str.substr(index, (re.lastIndex-m[0].length)-index);
  775.     if (pre)
  776.       parts.push(pre);
  777.  
  778.     let expr = m[1].split("|");
  779.     parts.push(new Variable(expr[0], expr.slice(1)));
  780.     index = re.lastIndex;
  781.   }
  782.  
  783.   if (!index)
  784.     return str;
  785.  
  786.   let post = str.substr(index);
  787.   if (post)
  788.     parts.push(post);
  789.  
  790.   return new Parts(parts);
  791. }
  792.  
  793. function parseValue(val)
  794. {
  795.   return typeof(val) == 'string' ? parseParts(val) : val;
  796. }
  797.  
  798. function parseChildren(args, offset, vars, children)
  799. {
  800.   for (let i = offset; i < args.length; ++i) {
  801.     let val = parseValue(args[i]);
  802.     children.push(val);
  803.     readPartNames(val, vars);
  804.   }
  805. }
  806.  
  807. function readPartNames(val, vars)
  808. {
  809.   if (val instanceof Parts) {
  810.     for (let i = 0; i < val.parts.length; ++i) {
  811.       let part = val.parts[i];
  812.       if (part instanceof Variable)
  813.         vars.push(part.name);
  814.     }
  815.   }
  816. }
  817.  
  818. function generateArg(val, path, args)
  819. {
  820.   if (val instanceof Parts) {
  821.     let vals = [];
  822.     for (let i = 0; i < val.parts.length; ++i) {
  823.       let part = val.parts[i];
  824.       if (part instanceof Variable) {
  825.         let varName = 'd' + path.renderIndex++;
  826.         if (part.format) {
  827.           for (let j = 0; j < part.format.length; ++j)
  828.             varName = part.format[j] + '(' + varName + ')';
  829.         }
  830.  
  831.         vals.push(varName);
  832.       }
  833.       else
  834.         vals.push('"' + part.replace(/"/g, '\\"') + '"');
  835.     }
  836.  
  837.     return vals.join('+');
  838.   } else {
  839.     args.push(val);
  840.     return 's' + path.staticIndex++;
  841.   }
  842. }
  843.  
  844. function addParts(val, delim, block, info, escapeIt)
  845. {
  846.   let vals = [];
  847.   if (val instanceof Parts) {
  848.     for (let i = 0; i < val.parts.length; ++i) {
  849.       let part = val.parts[i];
  850.       if (part instanceof Variable) {
  851.         let partName = part.name;
  852.         if (part.format) {
  853.           for (let j = 0; j < part.format.length; ++j)
  854.             partName = part.format[j] + "(" + partName + ")";
  855.         }
  856.  
  857.         if (escapeIt)
  858.           vals.push("__escape__(" + partName + ")");
  859.         else
  860.           vals.push(partName);
  861.       }
  862.       else
  863.         vals.push('"' + part + '"');
  864.     }
  865.   } else if (isTag(val)) {
  866.     info.args.push(val);
  867.     vals.push('s' + info.argIndex++);
  868.   } else
  869.     vals.push('"' + val + '"');
  870.  
  871.   let parts = vals.join(delim);
  872.   if (parts)
  873.     block.push(delim, parts);
  874. }
  875.  
  876. function isTag(obj)
  877. {
  878.   return (typeof(obj) == "function" || obj instanceof Function) && !!obj.tag;
  879. }
  880.  
  881. ///////////////////////////////////////////////////////////////////////////
  882. //// creator
  883.  
  884. function creator(tag, cons)
  885. {
  886.   let fn = new Function(
  887.     "var tag = arguments.callee.tag;" +
  888.     "var cons = arguments.callee.cons;" +
  889.     "var newTag = new cons();" +
  890.     "return newTag.merge(arguments, tag);");
  891.  
  892.   fn.tag = tag;
  893.   fn.cons = cons;
  894.   extend(fn, Renderer);
  895.  
  896.   return fn;
  897. }
  898.  
  899. ///////////////////////////////////////////////////////////////////////////
  900. //// Utility functions
  901.  
  902. function arrayInsert(array, index, other)
  903. {
  904.   for (let i = 0; i < other.length; ++i)
  905.     array.splice(i+index, 0, other[i]);
  906.  
  907.   return array;
  908. }
  909.  
  910. function cloneArray(array, fn)
  911. {
  912.   let newArray = [];
  913.  
  914.   if (fn)
  915.     for (let i = 0; i < array.length; ++i)
  916.       newArray.push(fn(array[i]));
  917.   else
  918.     for (let i = 0; i < array.length; ++i)
  919.       newArray.push(array[i]);
  920.  
  921.   return newArray;
  922. }
  923.  
  924. // fn, thisObject, args => thisObject.fn(args, arguments);
  925. function bind()
  926. {
  927.   let args = cloneArray(arguments), fn = args.shift(), object = args.shift();
  928.   return function bind()
  929.   {
  930.     return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments));
  931.   }
  932. }
  933.  
  934. function copyArray(oldArray)
  935. {
  936.   let array = [];
  937.   if (oldArray)
  938.     for (let i = 0; i < oldArray.length; ++i)
  939.       array.push(oldArray[i]);
  940.   return array;
  941. }
  942.  
  943. function copyObject(l, r)
  944. {
  945.   let m = {};
  946.   extend(m, l);
  947.   extend(m, r);
  948.   return m;
  949. }
  950.  
  951. function escapeNewLines(value)
  952. {
  953.   return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
  954. }
  955.  
  956. function extend(l, r)
  957. {
  958.   for (let n in r)
  959.     l[n] = r[n];
  960. }
  961.  
  962. function cropString(text, limit, alterText)
  963. {
  964.   if (!alterText)
  965.     alterText = "..."; //ΓǪ
  966.  
  967.   text = text + "";
  968.  
  969.   if (!limit)
  970.     limit = 88; // todo
  971.   var halfLimit = (limit / 2);
  972.   halfLimit -= 2; // adjustment for alterText's increase in size
  973.  
  974.   if (text.length > limit)
  975.     return text.substr(0, halfLimit) + alterText +
  976.       text.substr(text.length - halfLimit);
  977.   else
  978.     return text;
  979. }
  980.  
  981. function cropMultipleLines(text, limit)
  982. {
  983.   return escapeNewLines(this.cropString(text, limit));
  984. }
  985.  
  986. function isVisible(elt)
  987. {
  988.   if (elt.localName) {
  989.     return elt.offsetWidth > 0 || elt.offsetHeight > 0 ||
  990.       elt.localName.toLowerCase() in invisibleTags;
  991.   } else {
  992.     return elt.offsetWidth > 0 || elt.offsetHeight > 0;
  993.   }
  994.     // || isElementSVG(elt) || isElementMathML(elt);
  995. }
  996.  
  997. // Local Helpers
  998.  
  999. function isElementXHTML(node)
  1000. {
  1001.   return node.nodeName == node.nodeName.toLowerCase();
  1002. }
  1003.  
  1004. function isContainerElement(element)
  1005. {
  1006.   let tag = element.localName.toLowerCase();
  1007.   switch (tag) {
  1008.     case "script":
  1009.     case "style":
  1010.     case "iframe":
  1011.     case "frame":
  1012.     case "tabbrowser":
  1013.     case "browser":
  1014.       return true;
  1015.     case "link":
  1016.       return element.getAttribute("rel") == "stylesheet";
  1017.     case "embed":
  1018.       return element.getSVGDocument();
  1019.   }
  1020.   return false;
  1021. }
  1022.  
  1023. domplateUtils.isWhitespace = function isWhitespace(text)
  1024. {
  1025.   return !reNotWhitespace.exec(text);
  1026. };
  1027.  
  1028. domplateUtils.isWhitespaceText = function isWhitespaceText(node)
  1029. {
  1030.   if (node instanceof DOM.HTMLAppletElement)
  1031.     return false;
  1032.   return node.nodeType == DOM.Node.TEXT_NODE && this.isWhitespace(node.nodeValue);
  1033. }
  1034.  
  1035. function isSelfClosing(element)
  1036. {
  1037.   //if (isElementSVG(element) || isElementMathML(element))
  1038.   //    return true;
  1039.   var tag = element.localName.toLowerCase();
  1040.   return (selfClosingTags.hasOwnProperty(tag));
  1041. };
  1042.  
  1043. function isEmptyElement(element)
  1044. {
  1045.   if (showTextNodesWithWhitespace) {
  1046.     return !element.firstChild && isSelfClosing(element);
  1047.   } else {
  1048.     for (let child = element.firstChild; child; child = child.nextSibling) {
  1049.       if (!domplateUtils.isWhitespaceText(child))
  1050.         return false;
  1051.     }
  1052.   }
  1053.   return isSelfClosing(element);
  1054. }
  1055.  
  1056. function getEmptyElementTag(node)
  1057. {
  1058.   let isXhtml= isElementXHTML(node);
  1059.   if (isXhtml)
  1060.     return HTMLTemplates.XEmptyElement.tag;
  1061.   else
  1062.     return HTMLTemplates.EmptyElement.tag;
  1063. }
  1064.  
  1065. /**
  1066.  * Determines if the given node has any children which are elements.
  1067.  *
  1068.  * @param {Element} element Element to test.
  1069.  * @return true if immediate children of type Element exist, false otherwise
  1070.  */
  1071. function hasNoElementChildren(element)
  1072. {
  1073.   if (element.childElementCount != 0)
  1074.     return false;
  1075.  
  1076.   return true;
  1077. }
  1078.  
  1079. domplateUtils.getNodeTag = function getNodeTag(node, expandAll)
  1080. {
  1081.   if (node instanceof DOM.Element) {
  1082.     if (node instanceof DOM.HTMLHtmlElement && node.ownerDocument
  1083.         && node.ownerDocument.doctype)
  1084.       return HTMLTemplates.HTMLHtmlElement.tag;
  1085.     else if (node instanceof DOM.HTMLAppletElement)
  1086.       return getEmptyElementTag(node);
  1087.     else if (isContainerElement(node))
  1088.       return HTMLTemplates.Element.tag;
  1089.     else if (isEmptyElement(node))
  1090.       return getEmptyElementTag(node);
  1091.     else if (hasNoElementChildren(node))
  1092.       return HTMLTemplates.TextElement.tag;
  1093.     else
  1094.       return HTMLTemplates.Element.tag;
  1095.   }
  1096.   else if (node instanceof DOM.Text)
  1097.     return HTMLTemplates.TextNode.tag;
  1098.   else if (node instanceof DOM.CDATASection)
  1099.     return HTMLTemplates.CDATANode.tag;
  1100.   else if (node instanceof DOM.Comment)
  1101.     return HTMLTemplates.CommentNode.tag;
  1102.   else if (node instanceof DOM.SourceText)
  1103.     return HTMLTemplates.SourceText.tag;
  1104.   else
  1105.     return HTMLTemplates.Nada.tag;
  1106. }
  1107.  
  1108. function getNodeBoxTag(nodeBox)
  1109. {
  1110.   let re = /([^\s]+)NodeBox/;
  1111.   let m = re.exec(nodeBox.className);
  1112.   if (!m)
  1113.     return null;
  1114.  
  1115.   let nodeBoxType = m[1];
  1116.   if (nodeBoxType == "container")
  1117.     return HTMLTemplates.Element.tag;
  1118.   else if (nodeBoxType == "text")
  1119.     return HTMLTemplates.TextElement.tag;
  1120.   else if (nodeBoxType == "empty")
  1121.     return HTMLTemplates.EmptyElement.tag;
  1122. }
  1123.  
  1124. ///////////////////////////////////////////////////////////////////////////
  1125. //// ArrayIterator
  1126.  
  1127. function ArrayIterator(array)
  1128. {
  1129.   let index = -1;
  1130.  
  1131.   this.next = function()
  1132.   {
  1133.     if (++index >= array.length)
  1134.       throw StopIteration;
  1135.  
  1136.     return array[index];
  1137.   };
  1138. }
  1139.  
  1140. function StopIteration() {}
  1141.  
  1142. domplate.$break = function()
  1143. {
  1144.   throw StopIteration;
  1145. };
  1146.  
  1147. ///////////////////////////////////////////////////////////////////////////
  1148. //// Renderer
  1149.  
  1150. var Renderer =
  1151. {
  1152.   renderHTML: function(args, outputs, self)
  1153.   {
  1154.     let code = [];
  1155.     let markupArgs = [code, this.tag.getContext(), args, outputs];
  1156.     markupArgs.push.apply(markupArgs, this.tag.markupArgs);
  1157.     this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs);
  1158.     return code.join("");
  1159.   },
  1160.  
  1161.   insertRows: function(args, before, self)
  1162.   {
  1163.     if (!args)
  1164.       args = {};
  1165.  
  1166.     this.tag.compile();
  1167.  
  1168.     let outputs = [];
  1169.     let html = this.renderHTML(args, outputs, self);
  1170.  
  1171.     let doc = before.ownerDocument;
  1172.     let table = doc.createElement("table");
  1173.     table.innerHTML = html;
  1174.  
  1175.     let tbody = table.firstChild;
  1176.     let parent = before.localName.toLowerCase() == "tr" ? before.parentNode : before;
  1177.     let after = before.localName.toLowerCase() == "tr" ? before.nextSibling : null;
  1178.  
  1179.     let firstRow = tbody.firstChild, lastRow;
  1180.     while (tbody.firstChild) {
  1181.       lastRow = tbody.firstChild;
  1182.       if (after)
  1183.         parent.insertBefore(lastRow, after);
  1184.       else
  1185.         parent.appendChild(lastRow);
  1186.     }
  1187.  
  1188.     // To save the next poor soul:
  1189.     // In order to properly apply properties and event handlers on elements
  1190.     // constructed by a FOR tag, the tag needs to be able to iterate up and
  1191.     // down the tree, meaning if FOR is the root element as is the case with
  1192.     // many insertRows calls, it will need to iterator over portions of the
  1193.     // new parent.
  1194.     //
  1195.     // To achieve this end, __path__ defines the -1 operator which allows
  1196.     // parent traversal. When combined with the offset that we calculate
  1197.     // below we are able to iterate over the elements.
  1198.     //
  1199.     // This fails when applied to a non-loop element as non-loop elements
  1200.     // Do not generate to proper path to bounce up and down the tree.
  1201.     let offset = 0;
  1202.     if (this.tag.isLoop) {
  1203.       let node = firstRow.parentNode.firstChild;
  1204.       for (; node && node != firstRow; node = node.nextSibling)
  1205.         ++offset;
  1206.     }
  1207.  
  1208.     // strict warning: this.tag.context undefined
  1209.     let domArgs = [firstRow, this.tag.getContext(), offset];
  1210.     domArgs.push.apply(domArgs, this.tag.domArgs);
  1211.     domArgs.push.apply(domArgs, outputs);
  1212.  
  1213.     this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
  1214.     return [firstRow, lastRow];
  1215.   },
  1216.  
  1217.   insertBefore: function(args, before, self)
  1218.   {
  1219.     return this.insertNode(args, before.ownerDocument,
  1220.       function(frag) {
  1221.         before.parentNode.insertBefore(frag, before);
  1222.       }, self);
  1223.   },
  1224.  
  1225.   insertAfter: function(args, after, self)
  1226.   {
  1227.     return this.insertNode(args, after.ownerDocument,
  1228.       function(frag) {
  1229.         after.parentNode.insertBefore(frag, after.nextSibling);
  1230.       }, self);
  1231.   },
  1232.  
  1233.   insertNode: function(args, doc, inserter, self)
  1234.   {
  1235.     if (!args)
  1236.       args = {};
  1237.  
  1238.     this.tag.compile();
  1239.  
  1240.     let outputs = [];
  1241.     let html = this.renderHTML(args, outputs, self);
  1242.  
  1243.     let range = doc.createRange();
  1244.     range.selectNode(doc.body);
  1245.     let frag = range.createContextualFragment(html);
  1246.  
  1247.     let root = frag.firstChild;
  1248.     root = inserter(frag) || root;
  1249.  
  1250.     let domArgs = [root, this.tag.context, 0];
  1251.     domArgs.push.apply(domArgs, this.tag.domArgs);
  1252.     domArgs.push.apply(domArgs, outputs);
  1253.  
  1254.     this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
  1255.  
  1256.     return root;
  1257.   },
  1258.  
  1259.   replace: function(args, parent, self)
  1260.   {
  1261.     if (!args)
  1262.       args = {};
  1263.  
  1264.     this.tag.compile();
  1265.  
  1266.     let outputs = [];
  1267.     let html = this.renderHTML(args, outputs, self);
  1268.  
  1269.     let root;
  1270.     if (parent.nodeType == DOM.Node.ELEMENT_NODE) {
  1271.       parent.innerHTML = html;
  1272.       root = parent.firstChild;
  1273.     } else {
  1274.       if (!parent || parent.nodeType != DOM.Node.DOCUMENT_NODE)
  1275.         return;
  1276.  
  1277.       if (!womb || womb.ownerDocument != parent)
  1278.         womb = parent.createElement("div");
  1279.  
  1280.       womb.innerHTML = html;
  1281.  
  1282.       root = womb.firstChild;
  1283.     }
  1284.  
  1285.     let domArgs = [root, this.tag.context, 0];
  1286.     domArgs.push.apply(domArgs, this.tag.domArgs);
  1287.     domArgs.push.apply(domArgs, outputs);
  1288.     this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
  1289.  
  1290.     return root;
  1291.   },
  1292.  
  1293.   append: function(args, parent, self)
  1294.   {
  1295.     if (!args)
  1296.       args = {};
  1297.  
  1298.     this.tag.compile();
  1299.  
  1300.     let outputs = [];
  1301.     let html = this.renderHTML(args, outputs, self);
  1302.  
  1303.     if (!womb || womb.ownerDocument != parent.ownerDocument)
  1304.       womb = parent.ownerDocument.createElement("div");
  1305.     womb.innerHTML = html;
  1306.  
  1307.     let root = womb.firstChild;
  1308.     while (womb.firstChild)
  1309.       parent.appendChild(womb.firstChild);
  1310.  
  1311.     let domArgs = [root, this.tag.context, 0];
  1312.     domArgs.push.apply(domArgs, this.tag.domArgs);
  1313.     domArgs.push.apply(domArgs, outputs);
  1314.  
  1315.     this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
  1316.  
  1317.     return root;
  1318.   }
  1319. };
  1320.  
  1321. ///////////////////////////////////////////////////////////////////////////
  1322. //// defineTags macro
  1323.  
  1324. /**
  1325.  * Create default tags for a list of tag names.
  1326.  * @param Arguments
  1327.  *        list of string arguments
  1328.  */
  1329.  
  1330. function defineTags()
  1331. {
  1332.   for (let i = 0; i < arguments.length; ++i) {
  1333.     let tagName = arguments[i];
  1334.     let fn = new Function("var newTag = new DomplateTag('" + tagName +
  1335.       "'); return newTag.merge(arguments);");
  1336.  
  1337.     let fnName = tagName.toUpperCase();
  1338.     domplate[fnName] = fn;
  1339.   }
  1340. }
  1341.  
  1342. defineTags(
  1343.   "a", "button", "br", "canvas", "col", "colgroup", "div", "fieldset", "form",
  1344.   "h1", "h2", "h3", "hr", "img", "input", "label", "legend", "li", "ol",
  1345.   "optgroup", "option", "p", "pre", "select", "b", "span", "strong", "table",
  1346.   "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul", "iframe",
  1347.   "code"
  1348. );
  1349.  
  1350. ///////////////////////////////////////////////////////////////////////////
  1351. //// HTMLTemplates
  1352.  
  1353. let HTMLTemplates = {
  1354.   showTextNodesWithWhitespace: false
  1355. };
  1356.  
  1357. let BaseTemplates = {
  1358.   showTextNodesWithWhitespace: false
  1359. };
  1360.  
  1361. ///////////////////////////////////////////////////////////////////////////
  1362. //// HTMLTemplates.Reps
  1363.  
  1364. BaseTemplates.OBJECTLINK = domplate.A({
  1365.   "class": "objectLink objectLink-$className a11yFocus",
  1366.   _repObject: "$object"
  1367. });
  1368.  
  1369. BaseTemplates.Rep = domplate(
  1370. {
  1371.   className: "",
  1372.   inspectable: true,
  1373.  
  1374.   supportsObject: function(object, type)
  1375.   {
  1376.     return false;
  1377.   },
  1378.  
  1379.   inspectObject: function(object, context)
  1380.   {
  1381.     // Firebug.chrome.select(object);  // todo
  1382.   },
  1383.  
  1384.   browseObject: function(object, context)
  1385.   {
  1386.   },
  1387.  
  1388.   persistObject: function(object, context)
  1389.   {
  1390.   },
  1391.  
  1392.   getRealObject: function(object, context)
  1393.   {
  1394.     return object;
  1395.   },
  1396.  
  1397.   /**
  1398.    * Return a sensible string title for the given object, removing any wrapper
  1399.    * information from it.
  1400.    * @param aObject
  1401.    *        The object to get the title of.
  1402.    * @returns string
  1403.    */
  1404.  
  1405.   getTitle: function(aObject)
  1406.   {
  1407.     // e.g., [object XPCWrappedNative [object foo]]
  1408.     let label = safeToString(aObject);
  1409.  
  1410.     const re =/\[object ([^\]]*)/;
  1411.     let objectMatch = re.exec(label);
  1412.     let secondObjectMatch = null;
  1413.     if (objectMatch) {
  1414.       // e.g., XPCWrappedNative [object foo
  1415.       secondObjectMatch = re.exec(objectMatch[1]);
  1416.     }
  1417.  
  1418.     if (secondObjectMatch)
  1419.       return secondObjectMatch[1];  // eg foo
  1420.     else
  1421.       return objectMatch ? objectMatch[1] : label;
  1422.   },
  1423.  
  1424.   getTooltip: function(object)
  1425.   {
  1426.     return null;
  1427.   },
  1428.  
  1429.   /**
  1430.    * Called by chrome.onContextMenu to build the context menu when the
  1431.    * underlying object has this rep.
  1432.    * See also Panel for a similar function also called by onContextMenu.
  1433.    * Extensions may monkey patch and chain off this call.
  1434.    * @param object: the 'realObject', a model value, eg a DOM property
  1435.    * @param target: the HTML element clicked on.
  1436.    * @param context: the context, probably FirebugContext
  1437.    * @returns an array of menu items.
  1438.    */
  1439.   getContextMenuItems: function(object, target, context)
  1440.   {
  1441.     return [];
  1442.   },
  1443.  
  1444.   /////////////////////////////////////////////////////////////////////////
  1445.   // Convenience for domplates
  1446.  
  1447.   STR: function(name)
  1448.   {
  1449.     return name; // todo getproperty?
  1450.   },
  1451.  
  1452.   cropString: function(text)
  1453.   {
  1454.     return cropString(text);
  1455.   },
  1456.  
  1457.   cropMultipleLines: function(text, limit)
  1458.   {
  1459.     return cropMultipleLines(text, limit);
  1460.   },
  1461.  
  1462.   toLowerCase: function(text)
  1463.   {
  1464.     return text ? text.toLowerCase() : text;
  1465.   },
  1466.  
  1467.   plural: function(n)
  1468.   {
  1469.     return n == 1 ? "" : "s";
  1470.   }
  1471. });
  1472.  
  1473. BaseTemplates.Element = domplate(BaseTemplates.Rep,
  1474. {
  1475.   tag:
  1476.     BaseTemplates.OBJECTLINK(
  1477.       "<",
  1478.       domplate.SPAN({"class": "nodeTag"},
  1479.         "$object.localName|toLowerCase"),
  1480.       domplate.FOR("attr", "$object|attrIterator",
  1481.         " $attr.localName="",
  1482.         domplate.SPAN({"class": "nodeValue"},
  1483.           "$attr.nodeValue"),
  1484.         """
  1485.       ),
  1486.       ">"
  1487.     ),
  1488.  
  1489.   shortTag:
  1490.     BaseTemplates.OBJECTLINK(
  1491.       domplate.SPAN({"class": "$object|getVisible"},
  1492.         domplate.SPAN({"class": "selectorTag"},
  1493.           "$object|getSelectorTag"),
  1494.         domplate.SPAN({"class": "selectorId"},
  1495.           "$object|getSelectorId"),
  1496.         domplate.SPAN({"class": "selectorClass"},
  1497.           "$object|getSelectorClass"),
  1498.         domplate.SPAN({"class": "selectorValue"},
  1499.           "$object|getValue")
  1500.       )
  1501.     ),
  1502.  
  1503.   getVisible: function(elt)
  1504.   {
  1505.     return isVisible(elt) ? "" : "selectorHidden";
  1506.   },
  1507.  
  1508.   getSelectorTag: function(elt)
  1509.   {
  1510.     return elt.localName.toLowerCase();
  1511.   },
  1512.  
  1513.   getSelectorId: function(elt)
  1514.   {
  1515.     return elt.id ? ("#" + elt.id) : "";
  1516.   },
  1517.  
  1518.   getSelectorClass: function(elt)
  1519.   {
  1520.     return elt.getAttribute("class")
  1521.       ? ("." + elt.getAttribute("class").split(" ")[0])
  1522.       : "";
  1523.   },
  1524.  
  1525.   getValue: function(elt)
  1526.   { // todo getFileName
  1527.     let value;
  1528. /*
  1529.     if (elt instanceof HTMLImageElement)
  1530.       value = getFileName(elt.getAttribute("src"));
  1531.     else if (elt instanceof HTMLAnchorElement)
  1532.       value = getFileName(elt.getAttribute("href"));
  1533.     else if (elt instanceof HTMLInputElement)
  1534.       value = elt.getAttribute("value");
  1535.     else if (elt instanceof HTMLFormElement)
  1536.       value = getFileName(elt.getAttribute("action"));
  1537.     else if (elt instanceof HTMLScriptElement)
  1538.       value = getFileName(elt.getAttribute("src"));
  1539.  
  1540.     return value ? " " + cropMultipleLines(value, 20) : ""; */
  1541.     // trying a simplified version from above commented section
  1542.     // todo
  1543.     if (elt instanceof DOM.HTMLImageElement)
  1544.       value = elt.getAttribute("src");
  1545.     else if (elt instanceof DOM.HTMLAnchorElement)
  1546.       value = elt.getAttribute("href");
  1547.     else if (elt instanceof DOM.HTMLInputElement)
  1548.       value = elt.getAttribute("value");
  1549.     else if (elt instanceof DOM.HTMLFormElement)
  1550.       value = elt.getAttribute("action");
  1551.     else if (elt instanceof DOM.HTMLScriptElement)
  1552.       value = elt.getAttribute("src");
  1553.  
  1554.     return value ? " " + cropMultipleLines(value, 20) : "";
  1555.   },
  1556.  
  1557.   attrIterator: function(elt)
  1558.   {
  1559.     let attrs = [];
  1560.     let idAttr, classAttr;
  1561.     if (elt.attributes) {
  1562.       for (let i = 0; i < elt.attributes.length; ++i) {
  1563.         var attr = elt.attributes[i];
  1564.         if (attr.localName.indexOf("-moz-math") != -1)
  1565.           continue;
  1566.         else if (attr.localName == "id")
  1567.           idAttr = attr;
  1568.         else if (attr.localName == "class")
  1569.           classAttr = attr;
  1570.         else
  1571.           attrs.push(attr);
  1572.       }
  1573.     }
  1574.     if (classAttr)
  1575.       attrs.unshift(classAttr);
  1576.     if (idAttr)
  1577.       attrs.unshift(idAttr);
  1578.     return attrs;
  1579.   },
  1580.  
  1581.   shortAttrIterator: function(elt)
  1582.   {
  1583.     let attrs = [];
  1584.     if (elt.attributes) {
  1585.       for (let i = 0; i < elt.attributes.length; ++i) {
  1586.         let attr = elt.attributes[i];
  1587.           if (attr.localName == "id" || attr.localName == "class")
  1588.             attrs.push(attr);
  1589.       }
  1590.     }
  1591.  
  1592.     return attrs;
  1593.   },
  1594.  
  1595.   getHidden: function(elt)
  1596.   {
  1597.     return isVisible(elt) ? "" : "nodeHidden";
  1598.   },
  1599.  
  1600. /* getXPath: function(elt)
  1601.   {
  1602.     return getElementTreeXPath(elt); // todo
  1603.   }, */
  1604.  
  1605.   getNodeTextGroups: function(element)
  1606.   {
  1607.     let text =  element.textContent;
  1608.     return [{str: text, 'class': '', extra: ''}];
  1609.   },
  1610.  
  1611.   className: "element",
  1612.  
  1613.   supportsObject: function(object, type)
  1614.   {
  1615.     return object instanceof DOM.Element;
  1616.   },
  1617.  
  1618.   browseObject: function(elt, context)
  1619.   {
  1620.     let tag = elt.localName.toLowerCase();
  1621.     return true;
  1622.   },
  1623. });
  1624.  
  1625.  
  1626. ///////////////////////////////////////////////////////////////////////////
  1627. //// HTMLTemplates.tags
  1628.  
  1629. BaseTemplates.AttrTag =
  1630.   domplate.SPAN({"class": "nodeAttr editGroup"},
  1631.     " ",
  1632.     domplate.SPAN({"class": "nodeName editable"}, "$attr.nodeName"),
  1633.     "="",
  1634.     domplate.SPAN({"class": "nodeValue editable", "data-attributeName": "$attr.nodeName"}, "$attr.nodeValue"),
  1635.     """);
  1636.  
  1637. BaseTemplates.TextTag =
  1638.   domplate.SPAN({"class": "nodeText editable"},
  1639.     domplate.FOR("chr", "$object|getNodeTextGroups",
  1640.       domplate.SPAN({"class": "$chr.class $chr.extra"},
  1641.         "$chr.str")));
  1642.  
  1643. ///////////////////////////////////////////////////////////////////////////
  1644. //// HTMLTemplates
  1645.  
  1646.  
  1647.  
  1648. HTMLTemplates.CompleteElement = domplate(BaseTemplates.Element,
  1649. {
  1650.   tag:
  1651.     domplate.DIV({"class":
  1652.         "nodeBox open $object|getHidden repIgnore",
  1653.         _repObject: "$object", role : 'presentation'},
  1654.       domplate.DIV({"class": "nodeLabel", role: "presentation"},
  1655.         domplate.SPAN({"class": "nodeLabelBox repTarget repTarget",
  1656.           role : 'treeitem', 'aria-expanded' : 'false'},
  1657.           "<",
  1658.           domplate.SPAN({"class": "nodeTag"},
  1659.             "$object.nodeName|toLowerCase"),
  1660.           domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
  1661.           domplate.SPAN({"class": "nodeBracket"}, ">")
  1662.         )
  1663.       ),
  1664.       domplate.DIV({"class": "nodeChildBox", role :"group"},
  1665.         domplate.FOR("child", "$object|childIterator",
  1666.           domplate.TAG("$child|getNodeTag", {object: "$child"})
  1667.         )
  1668.       ),
  1669.       domplate.DIV({"class": "nodeCloseLabel", role:"presentation"},
  1670.         "</",
  1671.         domplate.SPAN({"class": "nodeTag"},
  1672.           "$object.nodeName|toLowerCase"),
  1673.         ">"
  1674.       )
  1675.     ),
  1676.  
  1677.   getNodeTag: function(node)
  1678.   {
  1679.     return domplateUtils.getNodeTag(node, true);
  1680.   },
  1681.  
  1682.   childIterator: function(node)
  1683.   {
  1684.     if (node.contentDocument)
  1685.       return [node.contentDocument.documentElement];
  1686.  
  1687.     if (this.showTextNodesWithWhitespace)
  1688.       return cloneArray(node.childNodes);
  1689.     else {
  1690.       let nodes = [];
  1691.       for (let child = node.firstChild; child; child = child.nextSibling) {
  1692.         if (child.nodeType != DOM.Node.TEXT_NODE || !domplateUtils.isWhitespaceText(child))
  1693.           nodes.push(child);
  1694.       }
  1695.       return nodes;
  1696.     }
  1697.   }
  1698. });
  1699.  
  1700. HTMLTemplates.SoloElement = domplate(HTMLTemplates.CompleteElement,
  1701. {
  1702.   tag:
  1703.     domplate.DIV({"class": "soloElement",
  1704.       onmousedown: "$onMouseDown"},
  1705.       HTMLTemplates.CompleteElement.tag),
  1706.  
  1707.   onMouseDown: function(event)
  1708.   {
  1709.     for (let child = event.target; child; child = child.parentNode) {
  1710.       if (child.repObject) { // todo
  1711.           // let panel = Firebug.getElementPanel(child);
  1712.           // Firebug.chrome.select(child.repObject);
  1713.           break;
  1714.       }
  1715.     }
  1716.   }
  1717. });
  1718.  
  1719. HTMLTemplates.Element = domplate(BaseTemplates.Element,
  1720. {
  1721.   tag:
  1722.     domplate.DIV({"class": "nodeBox containerNodeBox $object|getHidden repIgnore",
  1723.       _repObject: "$object", role: "presentation"},
  1724.       domplate.DIV({"class": "nodeLabel", role: "presentation"},
  1725.         domplate.IMG({"class": "twisty", role: "presentation"}),
  1726.         domplate.SPAN({"class": "nodeLabelBox repTarget",
  1727.           role: 'treeitem', 'aria-expanded': 'false'},
  1728.           "<",
  1729.           domplate.SPAN({"class": "nodeTag"},
  1730.             "$object.nodeName|toLowerCase"),
  1731.           domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
  1732.           domplate.SPAN({"class": "nodeBracket editable insertBefore"},
  1733.             ">")
  1734.         )
  1735.       ),
  1736.       domplate.DIV({"class": "nodeChildBox", role: "group"}), /* nodeChildBox is special signal in insideOutBox */
  1737.       domplate.DIV({"class": "nodeCloseLabel", role: "presentation"},
  1738.         domplate.SPAN({"class": "nodeCloseLabelBox repTarget"},
  1739.           "</",
  1740.           domplate.SPAN({"class": "nodeTag"}, "$object.nodeName|toLowerCase"),
  1741.           ">"
  1742.         )
  1743.       )
  1744.     )
  1745. });
  1746.  
  1747. HTMLTemplates.HTMLHtmlElement = domplate(BaseTemplates.Element,
  1748. {
  1749.   tag:
  1750.     domplate.DIV({"class":
  1751.         "nodeBox htmlNodeBox containerNodeBox $object|getHidden repIgnore",
  1752.         _repObject: "$object", role: "presentation"},
  1753.       domplate.DIV({"class": "docType"},
  1754.         "$object|getDocType"),
  1755.       domplate.DIV({"class": "nodeLabel", role: "presentation"},
  1756.         domplate.IMG({"class": "twisty", role: "presentation"}),
  1757.         domplate.SPAN({"class": "nodeLabelBox repTarget",
  1758.             role: 'treeitem', 'aria-expanded' : 'false'},
  1759.           "<",
  1760.           domplate.SPAN({"class": "nodeTag"},
  1761.             "$object.nodeName|toLowerCase"),
  1762.           domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
  1763.           domplate.SPAN({"class":
  1764.             "nodeBracket editable insertBefore"}, ">")
  1765.         )
  1766.       ), /* nodeChildBox is special signal in insideOutBox */
  1767.       domplate.DIV({"class": "nodeChildBox", role: "group"}),
  1768.       domplate.DIV({"class": "nodeCloseLabel", role:  "presentation"},
  1769.         domplate.SPAN({"class": "nodeCloseLabelBox repTarget"},
  1770.           "</",
  1771.           domplate.SPAN({"class": "nodeTag"},
  1772.             "$object.nodeName|toLowerCase"),
  1773.           ">"
  1774.         )
  1775.       )
  1776.     ),
  1777.  
  1778.   getDocType: function(obj)
  1779.   {
  1780.     let doctype = obj.ownerDocument.doctype;
  1781.     return '<!DOCTYPE ' + doctype.name + (doctype.publicId ? ' PUBLIC "' +
  1782.       doctype.publicId + '"': '') + (doctype.systemId ? ' "' +
  1783.       doctype.systemId + '"' : '') + '>';
  1784.   }
  1785. });
  1786.  
  1787. HTMLTemplates.TextElement = domplate(BaseTemplates.Element,
  1788. {
  1789.   tag:
  1790.     domplate.DIV({"class":
  1791.         "nodeBox textNodeBox $object|getHidden repIgnore",
  1792.         _repObject: "$object", role: 'presentation'},
  1793.       domplate.DIV({"class": "nodeLabel", role: "presentation"},
  1794.         domplate.SPAN({"class": "nodeLabelBox repTarget",
  1795.             role: 'treeitem'},
  1796.           "<",
  1797.           domplate.SPAN({"class": "nodeTag"},
  1798.             "$object.nodeName|toLowerCase"),
  1799.           domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
  1800.           domplate.SPAN({"class":
  1801.             "nodeBracket editable insertBefore"}, ">"),
  1802.           BaseTemplates.TextTag,
  1803.           "</",
  1804.           domplate.SPAN({"class": "nodeTag"},
  1805.             "$object.nodeName|toLowerCase"),
  1806.           ">"
  1807.         )
  1808.       )
  1809.     )
  1810. });
  1811.  
  1812. HTMLTemplates.EmptyElement = domplate(BaseTemplates.Element,
  1813. {
  1814.   tag:
  1815.     domplate.DIV({"class":
  1816.         "nodeBox emptyNodeBox $object|getHidden repIgnore",
  1817.         _repObject: "$object", role: 'presentation'},
  1818.       domplate.DIV({"class": "nodeLabel", role: "presentation"},
  1819.         domplate.SPAN({"class": "nodeLabelBox repTarget",
  1820.             role: 'treeitem'},
  1821.           "<",
  1822.           domplate.SPAN({"class": "nodeTag"},
  1823.             "$object.nodeName|toLowerCase"),
  1824.           domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
  1825.           domplate.SPAN({"class":
  1826.             "nodeBracket editable insertBefore"}, ">")
  1827.         )
  1828.       )
  1829.     )
  1830. });
  1831.  
  1832. HTMLTemplates.XEmptyElement = domplate(BaseTemplates.Element,
  1833. {
  1834.   tag:
  1835.     domplate.DIV({"class":
  1836.         "nodeBox emptyNodeBox $object|getHidden repIgnore",
  1837.         _repObject: "$object", role: 'presentation'},
  1838.       domplate.DIV({"class": "nodeLabel", role: "presentation"},
  1839.         domplate.SPAN({"class": "nodeLabelBox repTarget",
  1840.             role : 'treeitem'},
  1841.           "<",
  1842.           domplate.SPAN({"class": "nodeTag"},
  1843.             "$object.nodeName|toLowerCase"),
  1844.           domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
  1845.           domplate.SPAN({"class":
  1846.             "nodeBracket editable insertBefore"}, "/>")
  1847.         )
  1848.       )
  1849.     )
  1850. });
  1851.  
  1852. HTMLTemplates.AttrNode = domplate(BaseTemplates.Element,
  1853. {
  1854.   tag: BaseTemplates.AttrTag
  1855. });
  1856.  
  1857. HTMLTemplates.TextNode = domplate(BaseTemplates.Element,
  1858. {
  1859.   tag:
  1860.     domplate.DIV({"class": "nodeBox", _repObject: "$object",
  1861.       role: 'presentation'}, BaseTemplates.TextTag)
  1862. });
  1863.  
  1864. HTMLTemplates.CDATANode = domplate(BaseTemplates.Element,
  1865. {
  1866.   tag:
  1867.     domplate.DIV({"class": "nodeBox", _repObject: "$object",
  1868.       role: 'presentation'},
  1869.         "<![CDATA[",
  1870.         domplate.SPAN({"class": "nodeText nodeCDATA editable"},
  1871.           "$object.nodeValue"),
  1872.         "]]>")
  1873. });
  1874.  
  1875. HTMLTemplates.CommentNode = domplate(BaseTemplates.Element,
  1876. {
  1877.   tag:
  1878.     domplate.DIV({"class": "nodeBox nodeComment",
  1879.         _repObject: "$object", role : 'presentation'},
  1880.       "<!--",
  1881.       domplate.SPAN({"class": "nodeComment editable"},
  1882.         "$object.nodeValue"),
  1883.       "-->")
  1884. });
  1885.  
  1886. HTMLTemplates.Nada = domplate(BaseTemplates.Rep,
  1887. {
  1888.   tag: domplate.SPAN(""),
  1889.   className: "nada"
  1890. });
  1891.  
  1892.